﻿using D3D = Microsoft.DirectX.Direct3D;
using SimpleLiteForm.Graphics;
using SimpleLiteForm.References;
using SimpleLiteForm.Text;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using Microsoft.DirectX.Direct3D;
using SimpleLiteForm.Helpers;
using System.Globalization;

namespace SimpleLiteForm
{
    /// <summary>
    /// Represents the information popup control.
    /// </summary>
    public class InfoPopup : IDisposable
    {
        /// <summary>
        /// THe popup width.
        /// </summary>
        public const int WIDTH = 620;

        /// <summary>
        /// The popup heigth.
        /// </summary>
        public const int HEIGHT = 410;

        /// <summary>
        /// The left position.
        /// </summary>
        public const int POSITION_X = 10;

        /// <summary>
        /// The top position.
        /// </summary>
        public const int POSITION_Y = 10;

        /// <summary>
        /// The popup background.
        /// </summary>
        private RenderObject _background;

        /// <summary>
        /// The close button.
        /// </summary>
        private RenderObject _closeButton;

        /// <summary>
        /// The number of pregenerated link buttons.
        /// </summary>
        private const int LINK_BUTTON_COUNT = 8;

        /// <summary>
        /// The go buttons of the search results.
        /// </summary>
        private RenderObject[] _linkButtons = new RenderObject[LINK_BUTTON_COUNT];

        /// <summary>
        /// The original word.
        /// </summary>
        private string _originalWord;

        /// <summary>
        /// The tranlated word,
        /// </summary>
        private string _translatedWord;

        /// <summary>
        /// Indicates whether the popup was disposed.
        /// </summary>
        private bool _isDisposed;

        /// <summary>
        /// Indicates whether the popup is active.
        /// </summary>
        private bool _isActive;

        /// <summary>
        /// Creates a InfoPopup instance.
        /// </summary>
        /// <param name="device">The graphics device.</param>
        public InfoPopup(Device device)
        {
            _background = new RenderObject(device, "../../assets/textures/popup_back.png");
            _background.Size = new SizeF(WIDTH, HEIGHT);
            _background.Position = new PointF(POSITION_X, POSITION_Y);

            _closeButton = new RenderObject(device, "../../assets/textures/close.png");
            _closeButton.Size = new SizeF(32, 32);
            _closeButton.Position = new PointF(POSITION_X + WIDTH - 32, POSITION_Y);

            for (int i = 0; i < _linkButtons.Length; ++i)
            {
                _linkButtons[i] = new RenderObject(device, "../../assets/textures/go.png");
                _linkButtons[i].Size = new SizeF(20, 20);
            }
            HideLinkButtons();

            this.Close();
        }

        /// <summary>
        /// Renders the popup if it is active.
        /// </summary>
        public void Draw()
        {
            if (_isDisposed || !_isActive)
                return;

            int linkIndex = 0;

            _background.Draw();

            // translation
            FontHelper.TextTitle2.DrawText(null, "Word:", new Point(POSITION_X + 20, POSITION_Y + 15), Color.Black);
            FontHelper.TextSmall.DrawText(null, _originalWord, new Point(POSITION_X + 160, POSITION_Y + 18), Color.Black);
            FontHelper.TextTitle2.DrawText(null, "Translation:", new Point(POSITION_X + 20, POSITION_Y + 40), Color.Black);
            FontHelper.TextSmall.DrawText(null, _translatedWord, new Point(POSITION_X + 160, POSITION_Y + 43), Color.Black);

            // wiki
            FontHelper.TextTitle2.DrawText(null, "Wiki:", new Point(POSITION_X + 20, POSITION_Y + 70), Color.Black);
            Point wikiStartPosition = new Point(POSITION_X + 45, POSITION_Y + 93);

            if (WikiReferenceService.Instance.HasResult)
            {
                foreach (var wikiResult in WikiReferenceService.Instance.Results)
                {
                    // update link button
                    _linkButtons[linkIndex].Position = new PointF(wikiStartPosition.X - 25, wikiStartPosition.Y);
                    _linkButtons[linkIndex].Tag = wikiResult.Uri.OriginalString;
                    ++linkIndex;

                    wikiStartPosition = DrawTextMultiline(wikiResult.Content, wikiStartPosition, FontHelper.TextVerySmall, Color.Black, 90);
                }
            }
            else if (!WikiReferenceService.Instance.IsLoading)
            {
                FontHelper.TextVerySmall.DrawText(null, "No results.", wikiStartPosition, Color.Black);
            }
            else
            {
                FontHelper.TextVerySmall.DrawText(null, "Loading...", wikiStartPosition, Color.Black);
            }


            // bing search
            FontHelper.TextTitle2.DrawText(null, "Bing:", new Point(POSITION_X + 20, POSITION_Y + 165), Color.Black);
            Point bingStartPosition = new Point(POSITION_X + 45, POSITION_Y + 188);

            if (BingReferenceService.Instance.HasResult)
            {
                foreach (var bingResult in BingReferenceService.Instance.Results)
                {
                    // update link button
                    _linkButtons[linkIndex].Position = new PointF(bingStartPosition.X - 25, bingStartPosition.Y);
                    _linkButtons[linkIndex].Tag = bingResult.Uri.OriginalString;
                    ++linkIndex;

                    bingStartPosition = DrawTextMultiline(bingResult.Content, bingStartPosition, FontHelper.TextVerySmall, Color.Black, 90);
                }
            }
            else if (!BingReferenceService.Instance.IsLoading)
            {
                FontHelper.TextVerySmall.DrawText(null, "No results.", bingStartPosition, Color.Black);
            }
            else
            {
                FontHelper.TextVerySmall.DrawText(null, "Loading...", bingStartPosition, Color.Black);
            }

            for (int i = 0; i < linkIndex; ++i)
            {
                _linkButtons[i].Draw();
            }

            _closeButton.Draw();
        }

        /// <summary>
        /// Draws a text multilined.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <param name="topLeft">The top left position.</param>
        /// <param name="font">The font.</param>
        /// <param name="color">The color.</param>
        /// <param name="maxCharsPerLine">The max char count per line.</param>
        /// <returns>Returns the Point to continue with drawing.</returns>
        private Point DrawTextMultiline(string text, Point topLeft, D3D.Font font, Color color, int maxCharsPerLine)
        {
            int[] lineWrapPositions = new int[16];
            int splitCount = 0;
            int currentLineCounter = 0;
            int lastSplitIndex = -1;

            // clear array
            for (int i = 0; i < lineWrapPositions.Length; ++i)
                lineWrapPositions[i] = 0;

            // find sentence split location
            for (int i = 0; i < text.Length; ++i)
            {
                ++currentLineCounter;

                if (text[i] == ' ')
                {
                    lastSplitIndex = i;
                }

                if (currentLineCounter > maxCharsPerLine && lastSplitIndex != -1)
                {
                    lineWrapPositions[splitCount++] = lastSplitIndex;
                    lastSplitIndex = -1;
                    currentLineCounter = 0;
                }

                if (text[i] == '\n')
                {
                    lineWrapPositions[splitCount++] = i;
                    lastSplitIndex = -1;
                    currentLineCounter = 0;
                }
            }

            // draw all lines
            //int currentY = topLeft.Y;
            int startIndex = 0;
            for (int i = 0; i < splitCount; ++i)
            {
                string part = text.Substring(startIndex, lineWrapPositions[i] - startIndex);
                font.DrawText(null, part, topLeft, color);
                startIndex = lineWrapPositions[i] + 1;
                topLeft.Y += 16;
            }
            // and the last line
            string lastPart = text.Substring(startIndex, text.Length - startIndex);
            font.DrawText(null, lastPart, topLeft, color);
            topLeft.Y += 20;

            return topLeft;
        }

        /// <summary>
        /// Shows the popup.
        /// </summary>
        /// <param name="originalWord">The original word.</param>
        /// <param name="translatedWord">The translated word.</param>
        public void Show(string originalWord, string translatedWord)
        {
            _originalWord = originalWord;
            _translatedWord = translatedWord;

            HideLinkButtons();

            SearchReferencesAsync(translatedWord);

            _isActive = true;
        }

        /// <summary>
        /// Searches the references async.
        /// </summary>
        /// <param name="translatedWord">The translated word.</param>
        private void SearchReferencesAsync(string translatedWord)
        {
            // wiki reference
            WikiReferenceService.Instance.Search(translatedWord, Settings.PreferedLanguage);
            BingReferenceService.Instance.Search(translatedWord, Settings.PreferedLanguage);
        }

        /// <summary>
        /// Closes the popup.
        /// </summary>
        public void Close()
        {
            this._isActive = false;
            this._translatedWord = string.Empty;
            this._originalWord = string.Empty;
        }

        /// <summary>
        /// Handles the mouse click actions.
        /// </summary>
        /// <param name="location">The mous click location.</param>
        public void HandleClick(Point location)
        {
            if (!IsActive)
                return;

            if (_closeButton.Rectangle.Contains(location))
                this.Close();

            foreach (var link in _linkButtons)
            {
                if (link.Rectangle.Contains(location))
                {
                    ProcessHelper.StartWebsite(link.Tag);
                    break;
                }
            }
        }

        /// <summary>
        /// Hides all pregenerated link buttons out of screen.
        /// </summary>
        private void HideLinkButtons()
        {
            foreach (var button in _linkButtons)
            {
                button.Position = new PointF(-100, -100);
            }
        }
    
        /// <summary>
        /// Cleans up the unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            _background.Dispose();
            _closeButton.Dispose();

            foreach (var link in _linkButtons)
            {
                link.Dispose();
            }

            _isDisposed = true;
        }

        /// <summary>
        /// Gets whether the popup is active.
        /// </summary>
        public bool IsActive
        {
            get
            {
                return _isActive;
            }
        }
    }
}
